{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Communication Autoencoder with WGAN and pre-Autoencoder.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/BenYavor/Autoencoder_communication_system_WGAN_Channel-estimation/blob/master/Communication_Autoencoder_with_WGAN_and_pre_Autoencoder.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Y3Mvn1V30ejH",
        "colab_type": "code",
        "outputId": "6a80c9d3-727a-4c85-a43d-fe6fd122aa4d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 644
        }
      },
      "source": [
        "!pip install tensorflow==2.0.0\n",
        "import numpy as np\n",
        "%matplotlib inline\n",
        "import matplotlib.pyplot as plt   \n",
        "import warnings\n",
        "with warnings.catch_warnings():\n",
        "    warnings.filterwarnings(\"ignore\",category=FutureWarning)\n",
        "    import tensorflow as tf\n",
        "import os\n",
        "tf.__version__\n",
        "from tensorflow import keras\n",
        "import time\n",
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "import pandas as pd\n",
        "import sys\n",
        "assert sys.version_info >= (3, 5)\n",
        "%matplotlib inline\n",
        "import matplotlib as mpl\n",
        "mpl.rc('axes', labelsize=14)\n",
        "mpl.rc('xtick', labelsize=12)\n",
        "mpl.rc('ytick', labelsize=12)\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from sklearn.preprocessing import OneHotEncoder\n",
        "import pandas as pd\n",
        "from scipy import special\n",
        "#from Clustering_Equalgrps.equal_groups import EqualGroupsKMeans\n",
        "from tensorflow.keras import layers\n",
        "np.random.seed(42)\n",
        "tf.random.set_seed(42)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: tensorflow==2.0.0 in /usr/local/lib/python3.6/dist-packages (2.0.0)\n",
            "Requirement already satisfied: numpy<2.0,>=1.16.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.17.4)\n",
            "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.12.0)\n",
            "Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.8.1)\n",
            "Requirement already satisfied: keras-applications>=1.0.8 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.0.8)\n",
            "Requirement already satisfied: google-pasta>=0.1.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.1.8)\n",
            "Requirement already satisfied: tensorboard<2.1.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (2.0.2)\n",
            "Requirement already satisfied: tensorflow-estimator<2.1.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (2.0.1)\n",
            "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.1.0)\n",
            "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.33.6)\n",
            "Requirement already satisfied: opt-einsum>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (3.1.0)\n",
            "Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (3.10.0)\n",
            "Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.15.0)\n",
            "Requirement already satisfied: wrapt>=1.11.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.11.2)\n",
            "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.8.1)\n",
            "Requirement already satisfied: gast==0.2.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.2.2)\n",
            "Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.1.0)\n",
            "Requirement already satisfied: h5py in /usr/local/lib/python3.6/dist-packages (from keras-applications>=1.0.8->tensorflow==2.0.0) (2.8.0)\n",
            "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.1.1)\n",
            "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.16.0)\n",
            "Requirement already satisfied: google-auth<2,>=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.10.0)\n",
            "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (42.0.2)\n",
            "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.4.1)\n",
            "Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2.21.0)\n",
            "Requirement already satisfied: rsa<4.1,>=3.1.4 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (4.0)\n",
            "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (4.0.0)\n",
            "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.2.7)\n",
            "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.3.0)\n",
            "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2019.11.28)\n",
            "Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.24.3)\n",
            "Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2.8)\n",
            "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.0.4)\n",
            "Requirement already satisfied: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa<4.1,>=3.1.4->google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.4.8)\n",
            "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.1.0)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1wlZswcMF7Rt",
        "colab_type": "text"
      },
      "source": [
        "#### Vergleich\n",
        "Als erstes für feste $k$ und $n$, was sich ändert ist die Samplesize, Anzahl der Samples und SNR"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4qpY-gawAf-9",
        "colab_type": "text"
      },
      "source": [
        "###Systemparameter\n",
        "ACHTUNG: CHANNELANZAHL WURDE UNTERSCHIEDLICH VERWENDET \\\\\n",
        "$k$ - die Anzhal der bits \\\\\n",
        "$M$ - Anzahl der unterschiedlichen Nachrichten \\\\\n",
        "$n$ - channel uses\\\\\n",
        "$N$ - Länge des Rauschvektors"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "czeNNfpY1qc2",
        "outputId": "17b5b981-025b-40ad-f703-b4ff110a94d7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "k = 4      # Number of information bits per message, i.e., M=2**k\n",
        "M = 2**k\n",
        "n = 2    # Number of real channel uses per message\n",
        "#k = int(np.log2(M))\n",
        "#n = 2\n",
        "print(M)\n",
        "\n",
        "SNR = 7\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "16\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BA4TqJBOXXIg",
        "colab_type": "text"
      },
      "source": [
        "## Training Parameter"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "aJZ_cnp9V-Q6",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "gen_learning_rate=0.0001 \n",
        "disc_learning_rate = 0.0005                  # 0.0001  "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tb-DiBwSN255",
        "colab_type": "text"
      },
      "source": [
        "### Different Layers"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hFMMLrY0LthL",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "randN_initial = keras.initializers.RandomNormal(mean=0.0, stddev=0.05, seed=None)\n",
        "\n",
        "EncIn = tf.keras.layers.Input(shape=(M,))#, dtype= tf.int32)\n",
        "e1 = tf.keras.layers.Dense(2*n, activation=None)\n",
        "e2 = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,int(n/2),2]))\n",
        "EncOut = tf.keras.layers.Lambda(lambda x: x/tf.sqrt(2*tf.reduce_mean(tf.square(x))))\n",
        "GenIn = tf.keras.layers.Lambda(lambda x:tf.reshape(x,(tf.shape(x)[0],-1)))\n",
        "# = tf.keras.layers.Lambda(generator)\n",
        "DecIn = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,int(n/2),2]))\n",
        "d1 = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,n]))\n",
        "d2 = tf.keras.layers.Dense(M, activation='relu')\n",
        "DecOut = tf.keras.layers.Dense(M, activation='softmax')\n",
        "\n",
        "\n",
        "#noise_std = EbNo_to_noise(TRAINING_SNR)\n",
        "# custom functions / layers without weights\n",
        "norm_layer = keras.layers.Lambda(lambda x: tf.divide(x,tf.sqrt(2*tf.reduce_mean(tf.square(x)))))\n",
        "shape_layer = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2,n]))\n",
        "shape_layer2 = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,n]))\n",
        "channel_layer = keras.layers.Lambda(lambda x: x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7J96hJhKO9VJ",
        "colab_type": "text"
      },
      "source": [
        "### Help functions"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uV7pjryDv4M4",
        "colab_type": "code",
        "outputId": "fb75d7df-f32c-496a-e975-770ca332fc71",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "def EbNo2Sigma(ebnodb):\n",
        "    '''Convert Eb/No in dB to noise standard deviation'''\n",
        "    ebno = 10**(ebnodb/10)\n",
        "    return 1/np.sqrt(2*(2*k/n)*ebno)\n",
        "\n",
        "def EbNo_to_noise(ebnodb):\n",
        "    '''Transform EbNo[dB]/snr to noise power'''\n",
        "    ebno = 10**(ebnodb/10)\n",
        "    noise_std = 1/np.sqrt(2*(2*k/n)*ebno) \n",
        "    return noise_std\n",
        "\n",
        "\n",
        "def real_channel(x,noise_std):\n",
        "    # Black-box Channel\n",
        "    #AWGN\n",
        "    return x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std)\n",
        "\n",
        "def rayleigh_channel(x,noise_std):\n",
        "    return x + tf.sqrt(tf.square(tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std)) + tf.square(tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std)))\n",
        "    #Uniform U(-3;3)    \n",
        "    #return x + tf.random_uniform(tf.shape(x), minval=-2, maxval=2)\n",
        "\n",
        "def B_Ber(input_msg, msg):\n",
        "    '''Calculate the Batch Bit Error Rate'''\n",
        "    pred_error = tf.not_equal(tf.argmax(msg, 1), tf.argmax(input_msg, 1))\n",
        "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
        "    return bber\n",
        "\n",
        "def random_sample(batch_size=32):\n",
        "    msg = np.random.randint(M, size=batch_size)\n",
        "    return msg\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "def B_Ber_m(input_msg, msg):\n",
        "    '''Calculate the Batch Bit Error Rate'''\n",
        "    pred_error = tf.not_equal(input_msg, tf.argmax(msg, 1))      \n",
        "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
        "    return bber\n",
        "\n",
        "def SNR_to_noise(snrdb):\n",
        "    '''Transform EbNo[dB]/snr to noise power'''\n",
        "    snr = 10**(snrdb/10)\n",
        "    noise_std = 1/np.sqrt(2*snr)\n",
        "    return noise_std\n",
        "\n",
        "noise_std = EbNo2Sigma(SNR)\n",
        "\n",
        "print(EbNo2Sigma(SNR))\n",
        "print(EbNo_to_noise(SNR))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0.15792649852735607\n",
            "0.15792649852735607\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "OBGMgdDEh7uX",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def test_encoding(M=16, n=1):\n",
        "    inp = np.arange(0,M)\n",
        "    coding = gan_encoder.predict(inp)\n",
        "    fig = plt.figure(figsize=(4,4))\n",
        "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
        "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
        "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
        "    plt.grid(True)\n",
        "    plt.gca().set_ylim(-2, 2)\n",
        "    plt.gca().set_xlim(-2, 2)\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "def test_encoding_pre(M=16, n=1):\n",
        "    inp = np.arange(0,M)\n",
        "    coding = pre_encoder.predict(inp)\n",
        "    fig = plt.figure(figsize=(4,4))\n",
        "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
        "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
        "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
        "    plt.grid(True)\n",
        "    plt.gca().set_ylim(-2, 2)\n",
        "    plt.gca().set_xlim(-2, 2)\n",
        "    plt.show()    \n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AOoYuK_jR9rH",
        "colab_type": "text"
      },
      "source": [
        "# Models"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PQxhmgOa0_7c",
        "colab_type": "text"
      },
      "source": [
        "#### Generator Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LXbS5lM9Tb9B",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_generator(n):\n",
        "  input1 = tf.keras.layers.Input(shape=(n,))\n",
        "  x1 = tf.keras.layers.Dense(n)(input1)\n",
        "  input2 =tf.random.normal(shape=tf.shape(input1))\n",
        "  #input2 =tf.random.normal([tf.shape(input1)[0],n])\n",
        "  x2 = tf.keras.layers.Dense(n)(input2)\n",
        "  subtracted = tf.keras.layers.Concatenate(1)([x1, x2])\n",
        "  h1 = tf.keras.layers.Dense(64,use_bias=True,  activation='relu')(subtracted)\n",
        "  h2 = tf.keras.layers.Dense(64,use_bias=True, activation='relu')(h1)\n",
        "  out = tf.keras.layers.Dense(n, use_bias= True, activation='linear')(h2)\n",
        "  generator = tf.keras.models.Model(inputs=[input1], outputs=out)\n",
        "  return generator"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Vt2rTP7hSFt4",
        "colab_type": "text"
      },
      "source": [
        "#### Discriminator Model "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "97h2eMLeXS68",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_discriminator(n):\n",
        "  model = tf.keras.Sequential()\n",
        "  model.add(tf.keras.layers.Dense(32,use_bias=True, kernel_initializer=randN_initial,activation='relu',input_shape=((2*n,))))\n",
        "  model.add(tf.keras.layers.Dense(32,use_bias=True, kernel_initializer=randN_initial, activation='relu'))\n",
        "  model.add(tf.keras.layers.Dense(1,use_bias=False, activation='linear'))\n",
        "  return model\n",
        "\n",
        "number_of_disc_layers = 5"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6rIdAYRhHgGk",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "generator = get_generator(n)\n",
        "discriminator = get_discriminator(n)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lcIzLZj5Seh9",
        "colab_type": "text"
      },
      "source": [
        "## Encoder and Pre-Encoder Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sNHtzAC4SPBq",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_gan_encoder(M):\n",
        "  model = keras.models.Sequential([\n",
        "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal'),\n",
        "            keras.layers.Dense(M*2, activation=\"elu\"),\n",
        "            keras.layers.Dense(M*2, activation=\"elu\"),\n",
        "            keras.layers.Dense(n, activation=None),\n",
        "            e2,\n",
        "            EncOut,\n",
        "            GenIn])\n",
        "  return model\n",
        "\n",
        "def get_pre_encoder(M):\n",
        "  model = keras.models.Sequential([\n",
        "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal'),\n",
        "            keras.layers.Dense(M,kernel_initializer=randN_initial, activation=\"elu\"),\n",
        "            keras.layers.Dense(n,kernel_initializer=randN_initial, activation=None),\n",
        "            e2,\n",
        "            EncOut,\n",
        "            GenIn])\n",
        "  return model"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "G5iCDE4dSL35",
        "colab_type": "text"
      },
      "source": [
        "## Decoder and Pre-Decoder Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "C5KjEhDvSWQR",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_gan_decoder(M):\n",
        "   model= keras.models.Sequential([\n",
        "                #DecIn,\n",
        "                #d1,\n",
        "                keras.layers.Input(shape=(n,)),\n",
        "                keras.layers.Dense(M*2, activation=\"elu\"),\n",
        "                keras.layers.Dense(M*2, activation=\"elu\"),\n",
        "                keras.layers.Dense(M, activation=\"softmax\")\n",
        "                ])\n",
        "   return model\n",
        "\n",
        "\n",
        "def get_pre_decoder(M):\n",
        "   model= keras.models.Sequential([\n",
        "                #DecIn,\n",
        "                #d1,\n",
        "                keras.layers.Input(shape=(n,)),\n",
        "                keras.layers.Dense(M,kernel_initializer=randN_initial, activation=\"elu\"),\n",
        "                keras.layers.Dense(M,kernel_initializer=randN_initial, activation=\"softmax\")\n",
        "                ])\n",
        "   return model\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s-t76sYBtlP1",
        "colab_type": "text"
      },
      "source": [
        "### GAN Training functions"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "n15AvPFO05gd",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def gan_optimizers(gen_learning_rate  ,disc_learning_rate):\n",
        "  generator_optimizer = tf.keras.optimizers.RMSprop(gen_learning_rate)      #RMSprop   in oreder to test where the error comes from\n",
        "  discriminator_optimizer = tf.keras.optimizers.RMSprop(disc_learning_rate) \n",
        "  return generator_optimizer, discriminator_optimizer"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ooDukkHvmduJ",
        "colab_type": "code",
        "outputId": "fbba0d5a-cd60-4ca6-976f-5efc13883fb3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "%%time\n",
        "def train_gan(epochs,n_steps, batch_size, SNR_level):\n",
        "  noise_std = EbNo2Sigma(SNR_level)\n",
        "  start = time.time()\n",
        "  x = tf.random.normal((batch_size,n),dtype=tf.dtypes.float32) \n",
        "  x = x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  counter = 0\n",
        "  epoch = 0\n",
        "  for epoch in range(epochs):\n",
        "    counter += 1\n",
        "    train_step(noise_std, n_steps,batch_size)\n",
        "    if counter%100==0:\n",
        "      tf.print(\"counter %d:\" % (counter))\n",
        "      fake_c = generator(x)\n",
        "      tf.print(fake_c[0])\n",
        "    #print ('Time for epoch {} is {} sec,'.format(epoch + 1, time.time()-start))\n",
        "      tf.print ('Time for epoch {},'.format(epoch + 1))\n",
        "      \n",
        "  tf.saved_model.save(generator,'/tmp/saved_model/')\n",
        "  tf.print ('Time for the training is {} sec,'.format( time.time()-start))\n",
        "    "
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "CPU times: user 3 µs, sys: 1 µs, total: 4 µs\n",
            "Wall time: 6.91 µs\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "WE_JS7kgA1W-",
        "colab": {}
      },
      "source": [
        "@tf.function\n",
        "def train_step(noise_std,n_steps,batch_size):\n",
        "  x = tf.random.normal((batch_size,n),dtype=tf.dtypes.float32) \n",
        "  x = x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  m =random_sample(batch_size)\n",
        "  r = pre_encoder(m)\n",
        "  for i in range(n_steps):\n",
        "    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n",
        "      real_training_data = tf.concat(values=[real_channel(r,noise_std), r], axis=1)\n",
        "      fake_training_data = tf.concat(values=[generator(r),r], axis=1)\n",
        "      real_output = discriminator(real_training_data)\n",
        "      fake_output = discriminator(fake_training_data)\n",
        "      \n",
        "      \n",
        "      #disc_loss = -tf.reduce_mean(tf.math.log(real_output) + tf.math.log(1. - fake_output))\n",
        "      #gen_loss =-tf.reduce_mean(tf.math.log(fake_output))\n",
        "      \n",
        "      disc_loss = tf.reduce_mean(real_output) - tf.reduce_mean(fake_output)\n",
        "      gen_loss = -tf.reduce_mean(fake_output)\n",
        "      #tf.print(disc_loss,gen_loss)\n",
        "      \n",
        "      if tf.math.is_nan(disc_loss) == False:\n",
        "        gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n",
        "        discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))\n",
        "        i=0\n",
        "        for i in range(number_of_disc_layers):\n",
        "          y = tf.clip_by_value(discriminator.trainable_variables[i],clip_value_min=-0.01,clip_value_max=0.01,name=None)\n",
        "          discriminator.trainable_variables[i].assign(y)\n",
        "\n",
        "        #tf.print(discriminator.trainable_weights[1])\n",
        "\n",
        "    \n",
        "      if i == 4:  \n",
        "        gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n",
        "        generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))\n",
        "\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "y82FQj3Jmvxx",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def creating_and_train_gan(epochs,n_steps, batch_size, SNR_level , n ):  #optional Leraning Rates\n",
        "  train_gan(epochs, n_steps, batch_size, SNR_level)\n",
        "  #4 after GAN training\n",
        "  generator.trainable = False\n",
        "  tf.print(generator.trainable)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dLTtFoV0IoPj",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def gan_Test_AE(data):\n",
        "    '''Calculate Bit Error for varying SNRs'''\n",
        "    snr_range = np.linspace(0, 15, 31)\n",
        "    bber_vec = [None] * len(snr_range)\n",
        "        \n",
        "    for db in range(len(snr_range)):           \n",
        "        noise_std = EbNo_to_noise(snr_range[db])\n",
        "        code_word = gan_encoder(data)\n",
        "        rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
        "        dcoded_msg = gan_decoder(rcvd_word)\n",
        "        bber_vec[db] = B_Ber_m(data, dcoded_msg)\n",
        "        if (db % 6 == 0) & (db > 0):\n",
        "            print(f'Progress: {db} of {30} parts')\n",
        "\n",
        "    return (snr_range, bber_vec)\n",
        "\n",
        "def Test_AE_rayleigh(data):\n",
        "    '''Calculate Bit Error for varying SNRs'''\n",
        "    snr_range = np.linspace(0, 15, 31)\n",
        "    bber_vec = [None] * len(snr_range)\n",
        "        \n",
        "    for db in range(len(snr_range)):           \n",
        "        noise_std = EbNo_to_noise(snr_range[db])\n",
        "        code_word = gan_encoder(data)\n",
        "        rcvd_word = rayleigh_channel(code_word, noise_std)\n",
        "        dcoded_msg = gan_decoder(rcvd_word)\n",
        "        bber_vec[db] = B_Ber_m(data, dcoded_msg)\n",
        "        if (db % 6 == 0) & (db > 0):\n",
        "            print(f'Progress: {db} of {30} parts')\n",
        "\n",
        "    return (snr_range, bber_vec)\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VI0Tj40kzY6X",
        "colab_type": "text"
      },
      "source": [
        "# Pre Autoencoder Training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "eS0X_xB4LBXa",
        "colab_type": "code",
        "outputId": "aa971fad-5683-494f-88da-c97437ad4227",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 652
        }
      },
      "source": [
        "pre_decoder = get_pre_decoder(M)\n",
        "pre_encoder = get_pre_encoder(M)\n",
        "\n",
        "\n",
        "\n",
        "gan_AE = tf.keras.models.Sequential([pre_encoder,pre_decoder])\n",
        "data, test_data = random_sample(10000000), random_sample(10000)\n",
        "start = time.time()\n",
        "gan_AE.compile(optimizer=keras.optimizers.Nadam(lr=0.005),loss='sparse_categorical_crossentropy',metrics=['accuracy'])\n",
        "history = gan_AE.fit(data, data, batch_size=500,steps_per_epoch=400, epochs=5)\n",
        "#pre_encoder.trainable = False\n",
        "#pre_decoder.trainable = False\n",
        "gan_AE.summary() \n",
        "test_encoding_pre(M,n) \n",
        "\n",
        "print(data.shape)\n",
        "print(pre_encoder(data).shape)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Train on 10000000 samples\n",
            "Epoch 1/5\n",
            "  196000/10000000 [..............................] - ETA: 2:46 - loss: 1.0680 - accuracy: 0.7182Epoch 2/5\n",
            "  194000/10000000 [..............................] - ETA: 54s - loss: 0.0415 - accuracy: 1.0000Epoch 3/5\n",
            "  200000/10000000 [..............................] - ETA: 55s - loss: 0.0098 - accuracy: 1.0000Epoch 4/5\n",
            "  195500/10000000 [..............................] - ETA: 54s - loss: 0.0043 - accuracy: 1.0000Epoch 5/5\n",
            "  197000/10000000 [..............................] - ETA: 53s - loss: 0.0024 - accuracy: 1.0000Model: \"sequential_3\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "sequential_2 (Sequential)    (None, None)              562       \n",
            "_________________________________________________________________\n",
            "sequential_1 (Sequential)    (None, 16)                320       \n",
            "=================================================================\n",
            "Total params: 882\n",
            "Trainable params: 882\n",
            "Non-trainable params: 0\n",
            "_________________________________________________________________\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nwjCC6fZfAAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<Figure size 288x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "(10000000,)\n",
            "(10000000, 2)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "q4AmiQPMd1ea",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3m0fQ6OXgPf1",
        "colab_type": "text"
      },
      "source": [
        "# GAN Training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "nNFwwZrcgOkn",
        "colab_type": "code",
        "outputId": "cd95013a-016d-4a80-eed1-690c8362087d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        }
      },
      "source": [
        "%%time\n",
        "\n",
        "generator_optimizer, discriminator_optimizer = gan_optimizers(gen_learning_rate=gen_learning_rate, disc_learning_rate = disc_learning_rate)\n",
        "start = time.time()\n",
        "creating_and_train_gan(epochs= 5000,n_steps=5, batch_size =100, SNR_level = 7, n = n)\n",
        "time_to_train_gan = time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format( time.time()-start))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "counter 100:\n",
            "[2.58380437 -1.87082231]\n",
            "Time for epoch 100,\n",
            "counter 200:\n",
            "[9.40456772 -7.92278719]\n",
            "Time for epoch 200,\n",
            "counter 300:\n",
            "[24.1549892 -21.8296032]\n",
            "Time for epoch 300,\n",
            "counter 400:\n",
            "[59.2203522 -55.2618713]\n",
            "Time for epoch 400,\n",
            "counter 500:\n",
            "[111.6828 -105.999519]\n",
            "Time for epoch 500,\n",
            "counter 600:\n",
            "[378.433624 -366.051971]\n",
            "Time for epoch 600,\n",
            "counter 700:\n",
            "[475.860321 -462.658051]\n",
            "Time for epoch 700,\n",
            "counter 800:\n",
            "[662.516907 -643.456665]\n",
            "Time for epoch 800,\n",
            "counter 900:\n",
            "[1295.70654 -1268.31299]\n",
            "Time for epoch 900,\n",
            "counter 1000:\n",
            "[1030.07007 -1007.98645]\n",
            "Time for epoch 1000,\n",
            "counter 1100:\n",
            "[2200.53198 -2162.71826]\n",
            "Time for epoch 1100,\n",
            "counter 1200:\n",
            "[1982.95142 -1948.46069]\n",
            "Time for epoch 1200,\n",
            "counter 1300:\n",
            "[3006.29468 -2961.51221]\n",
            "Time for epoch 1300,\n",
            "counter 1400:\n",
            "[3472.93848 -3422.09]\n",
            "Time for epoch 1400,\n",
            "counter 1500:\n",
            "[4959.77441 -4895.61865]\n",
            "Time for epoch 1500,\n",
            "counter 1600:\n",
            "[5591.80078 -5520.64941]\n",
            "Time for epoch 1600,\n",
            "counter 1700:\n",
            "[7540.65234 -7453.58594]\n",
            "Time for epoch 1700,\n",
            "counter 1800:\n",
            "[20148.9434 -19939.9219]\n",
            "Time for epoch 1800,\n",
            "counter 1900:\n",
            "[12370.5293 -12247.0234]\n",
            "Time for epoch 1900,\n",
            "counter 2000:\n",
            "[15589.9258 -15443.292]\n",
            "Time for epoch 2000,\n",
            "counter 2100:\n",
            "[27516.0664 -27271.3125]\n",
            "Time for epoch 2100,\n",
            "counter 2200:\n",
            "[18007.6973 -17844.0723]\n",
            "Time for epoch 2200,\n",
            "counter 2300:\n",
            "[21223.7324 -21039.7383]\n",
            "Time for epoch 2300,\n",
            "counter 2400:\n",
            "[28008.918 -27784.623]\n",
            "Time for epoch 2400,\n",
            "counter 2500:\n",
            "[31135.9316 -30893.5801]\n",
            "Time for epoch 2500,\n",
            "counter 2600:\n",
            "[38431.5547 -38148.9727]\n",
            "Time for epoch 2600,\n",
            "counter 2700:\n",
            "[45069.2656 -44751.5742]\n",
            "Time for epoch 2700,\n",
            "counter 2800:\n",
            "[47887.0508 -47555.7695]\n",
            "Time for epoch 2800,\n",
            "counter 2900:\n",
            "[56066.8281 -55694.7812]\n",
            "Time for epoch 2900,\n",
            "counter 3000:\n",
            "[56241.7812 -55870.7539]\n",
            "Time for epoch 3000,\n",
            "counter 3100:\n",
            "[80148.3359 -79662]\n",
            "Time for epoch 3100,\n",
            "counter 3200:\n",
            "[136822.578 -136026.172]\n",
            "Time for epoch 3200,\n",
            "counter 3300:\n",
            "[97856.4844 -97301.8516]\n",
            "Time for epoch 3300,\n",
            "counter 3400:\n",
            "[95510.0078 -94973.5703]\n",
            "Time for epoch 3400,\n",
            "counter 3500:\n",
            "[121943.898 -121307.453]\n",
            "Time for epoch 3500,\n",
            "counter 3600:\n",
            "[122179.07 -121551.188]\n",
            "Time for epoch 3600,\n",
            "counter 3700:\n",
            "[153789.578 -153050.016]\n",
            "Time for epoch 3700,\n",
            "counter 3800:\n",
            "[152479.859 -151756.453]\n",
            "Time for epoch 3800,\n",
            "counter 3900:\n",
            "[166069.469 -165307.516]\n",
            "Time for epoch 3900,\n",
            "counter 4000:\n",
            "[208398.031 -207499.719]\n",
            "Time for epoch 4000,\n",
            "counter 4100:\n",
            "[208128.906 -207244.969]\n",
            "Time for epoch 4100,\n",
            "counter 4200:\n",
            "[228206.125 -227257.797]\n",
            "Time for epoch 4200,\n",
            "counter 4300:\n",
            "[230591.266 -229635.266]\n",
            "Time for epoch 4300,\n",
            "counter 4400:\n",
            "[501304.281 -499365.375]\n",
            "Time for epoch 4400,\n",
            "counter 4500:\n",
            "[392094.781 -390606.25]\n",
            "Time for epoch 4500,\n",
            "counter 4600:\n",
            "[365574.469 -364193.875]\n",
            "Time for epoch 4600,\n",
            "counter 4700:\n",
            "[347454.969 -346136.688]\n",
            "Time for epoch 4700,\n",
            "counter 4800:\n",
            "[435880.375 -434292.344]\n",
            "Time for epoch 4800,\n",
            "counter 4900:\n",
            "[439028.219 -437438.594]\n",
            "Time for epoch 4900,\n",
            "counter 5000:\n",
            "[437104.344 -435529.156]\n",
            "Time for epoch 5000,\n",
            "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\n",
            "Instructions for updating:\n",
            "If using Keras pass *_constraint arguments to layers.\n",
            "INFO:tensorflow:Assets written to: /tmp/saved_model/assets\n",
            "Time for the training is 32.42557621002197 sec,\n",
            "False\n",
            "Time for the training is 32.4294798374176 sec,\n",
            "CPU times: user 49.9 s, sys: 1.14 s, total: 51.1 s\n",
            "Wall time: 32.4 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6GW2opX7SwMo",
        "colab_type": "text"
      },
      "source": [
        "\n",
        "# AE training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FiuN3SZYpeTU",
        "colab_type": "code",
        "outputId": "1d13382b-e950-4d4c-de5d-29332b1c00b7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 537
        }
      },
      "source": [
        "%%time\n",
        "\n",
        "gan_decoder = get_gan_decoder(M)\n",
        "gan_encoder = get_gan_encoder(M)\n",
        "\n",
        "\n",
        "\n",
        "gan_AE = tf.keras.models.Sequential([gan_encoder,generator,gan_decoder])\n",
        "data, test_data = random_sample(10000000), random_sample(10000)\n",
        "start = time.time()\n",
        "gan_AE.compile(optimizer=keras.optimizers.Nadam(lr=0.001),loss='sparse_categorical_crossentropy',metrics=['accuracy'])\n",
        "history = gan_AE.fit(data, data, batch_size=500,steps_per_epoch=400, epochs=10)\n",
        "time_to_train_gan += time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format( time.time()-start))\n",
        "gan_AE.summary()  "
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Train on 10000000 samples\n",
            "Epoch 1/10\n",
            "  198000/10000000 [..............................] - ETA: 3:38 - loss: 8038.7049 - accuracy: 0.0683Epoch 2/10\n",
            "  199500/10000000 [..............................] - ETA: 2:03 - loss: 1376.6982 - accuracy: 0.0667Epoch 3/10\n",
            "  198000/10000000 [..............................] - ETA: 1:52 - loss: 1279.9490 - accuracy: 0.0656Epoch 4/10\n",
            "  197500/10000000 [..............................] - ETA: 1:50 - loss: 1179.4701 - accuracy: 0.0651Epoch 5/10\n",
            "  198500/10000000 [..............................] - ETA: 1:53 - loss: 1089.1374 - accuracy: 0.0645Epoch 6/10\n",
            "  200000/10000000 [..............................] - ETA: 1:51 - loss: 977.1957 - accuracy: 0.0649Epoch 7/10\n",
            "  199000/10000000 [..............................] - ETA: 1:51 - loss: 861.7340 - accuracy: 0.0656Epoch 8/10\n",
            "  199500/10000000 [..............................] - ETA: 1:56 - loss: 799.5723 - accuracy: 0.0657Epoch 9/10\n",
            "  200000/10000000 [..............................] - ETA: 1:56 - loss: 724.0799 - accuracy: 0.0681Epoch 10/10\n",
            "  196500/10000000 [..............................] - ETA: 1:53 - loss: 679.0939 - accuracy: 0.0675Time for the training is 25.55487084388733 sec,\n",
            "Model: \"sequential_6\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "sequential_5 (Sequential)    (None, None)              1922      \n",
            "_________________________________________________________________\n",
            "model (Model)                (None, 2)                 4622      \n",
            "_________________________________________________________________\n",
            "sequential_4 (Sequential)    (None, 16)                1680      \n",
            "=================================================================\n",
            "Total params: 8,224\n",
            "Trainable params: 3,602\n",
            "Non-trainable params: 4,622\n",
            "_________________________________________________________________\n",
            "CPU times: user 31.9 s, sys: 952 ms, total: 32.9 s\n",
            "Wall time: 25.9 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ngrucnfWBOHl",
        "colab_type": "text"
      },
      "source": [
        "### Training MI"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "D5B2TUanPC5d",
        "colab_type": "code",
        "outputId": "569448a0-fd79-4396-bc15-5ff68667ee6a",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 295
        }
      },
      "source": [
        "gan_encoder.trainable = False\n",
        "gan_decoder.trainable = False\n",
        "\n",
        "test_encoding(M,n)   \n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nAAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<Figure size 288x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ukO76l6yIoPc",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# test msg sequence for normal encoding\n",
        "N_test = 500000\n",
        "test_msg = np.random.randint(M, size=N_test)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PK5wA-zzHScv",
        "colab_type": "text"
      },
      "source": [
        "### Comparison"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7M-S0sbhIoPw",
        "colab_type": "code",
        "outputId": "f5898788-5fa9-43aa-bd11-ba142ae9e28d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 196
        }
      },
      "source": [
        "gan_bber_data = gan_Test_AE(test_msg)\n",
        "bber_data_rayleigh = Test_AE_rayleigh(test_msg)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Progress: 6 of 30 parts\n",
            "Progress: 12 of 30 parts\n",
            "Progress: 18 of 30 parts\n",
            "Progress: 24 of 30 parts\n",
            "Progress: 30 of 30 parts\n",
            "Progress: 6 of 30 parts\n",
            "Progress: 12 of 30 parts\n",
            "Progress: 18 of 30 parts\n",
            "Progress: 24 of 30 parts\n",
            "Progress: 30 of 30 parts\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "UYdEm0eQIoP2",
        "colab_type": "code",
        "outputId": "948a82aa-37b6-4422-809e-fde368d86cc3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 370
        }
      },
      "source": [
        "# Approximate 16 QAM Error\n",
        "def SIXT_QAM_sim(ebno):\n",
        "    return (3.0/2)*special.erfc(np.sqrt((4.0/10)*10.**(ebno/10)))\n",
        "\n",
        "ebnodbs = np.linspace(0,15,16)\n",
        "fig = plt.figure(figsize=(8, 5))\n",
        "plt.semilogy(gan_bber_data[0], gan_bber_data[1], '^-')\n",
        "plt.semilogy(ebnodbs, SIXT_QAM_sim(ebnodbs), '*-');\n",
        "plt.gca().set_ylim(1e-5, 1)\n",
        "plt.gca().set_xlim(0, 15)\n",
        "plt.ylabel(\"Batch Symbol Error Rate\", fontsize=14, rotation=90)\n",
        "plt.xlabel(\"SNR [dB]\", fontsize=18)\n",
        "plt.legend(['AE with GAN', '16QAM'],\n",
        "           prop={'size': 14}, loc='upper right');\n",
        "plt.grid(True, which=\"both\")\n",
        "print('time to train the AE Model with GAN',time_to_train_gan)\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "time to train the AE Model with GAN 57.98433995246887\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 576x360 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bBes21qLlcS8",
        "colab_type": "code",
        "outputId": "9de93f13-bdbc-46e2-8b6c-dcefce547486",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 352
        }
      },
      "source": [
        "def SIXT_QAM_sim(ebno):\n",
        "    return (3.0/2)*special.erfc(np.sqrt((4.0/10)*10.**(ebno/10)))\n",
        "\n",
        "ebnodbs = np.linspace(0,15,16)\n",
        "fig = plt.figure(figsize=(8, 5))\n",
        "plt.semilogy(gan_bber_data[0], gan_bber_data[1], 'o-')\n",
        "plt.semilogy(bber_data_rayleigh[0], bber_data_rayleigh[1], '+-')\n",
        "#plt.semilogy(ebnodbs, SIXT_QAM_sim(ebnodbs), '^-');\n",
        "plt.gca().set_ylim(1e-5, 1)\n",
        "plt.gca().set_xlim(0, 15)\n",
        "plt.ylabel(\"Batch Symbol Error Rate\", fontsize=14, rotation=90)\n",
        "plt.xlabel(\"SNR [dB]\", fontsize=18)\n",
        "plt.legend(['BLER for Gauss','BLER for Rayleigh'],\n",
        "           prop={'size': 14}, loc='upper right');\n",
        "plt.grid(True, which=\"both\")"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nCeXHGjRoQOfOnTn//PP57LPPao3xxz/+McOHD2f58uXccccd+550JSUlJYwfP54+ffrQrFkz2rZt\nyzHHHMPkyZNZv37vtf3la2oMiIiExJAhQ1i9ejWrV6/m+eefZ9u2bZx11ll1rmfhwoXl9ZS9evTo\nUX6+qKiI1atXs2LFCmbMmMGCBQs455xzaqxzw4YNfPHFFwwdOpQuXbrQqlWrOscFsGPHjiqPf/XV\nV3zjG9/gvvvu48orr+Tvf/87//znP/nNb37D4sWLuf/+++t1v6iI1KJDcWMGmDt3bqbDSZvNmzdH\nJt8o5QrKNxVatWpFSUnJPtcz6/3PueOVZazZVErHlo35xaDufKtPLTvqxdm9e3ed4ti5cyd5eXk0\nb94cgB49enDppZfy/e9/n7Vr11aYIbFt27Yq6966dSsAzZo1K6+n8rnS0lIaNGhQfv6oo47ihz/8\nIVdffTUrV66sctGb119/nW9961sAnHpqsLHOrFmz6N+/P88++yyTJ09m6dKltG/fnpEjR3LVVVeV\nD4br06cP5513HitWrOC5555j0KBBPPzww3vdY/To0Sxbtox3332XTp06lR8//vjjOf7443H38pyn\nT5/O3XffzZIlS2jSpAknn3wyN998M507d64Q7yeffMJ++wXbBS9fvpzDDz+cuXPn0rdvX3bu3Mmv\nf/1rnnnmGb788kvat2/POeecw8SJEwF49tlnuemmm/j3v/9NkyZN6N27Nw899BCFhYV1/reti+3b\nt9fr/5FINQbKdi0sKioapX7WcIpSrqB8U2HRokX73J/79D9XMnH20vLNYVZvKmXi7KU0adI0oQ18\noO79yvn5+TRs2LC8TElJCc899xyHH344hYUVt7lt2rRplXWXra3QokWLau/duHFjGjRoUH5+zZo1\nzJ49m7y8PFq3br1XIwKCJxYLFy6kd+/ezJw5k5NOOom2bduyYMECfvSjHzFu3DjOPPNMFi1axKWX\nXkr79u352c9+BgTdGnfddRfjxo1j/PjxuPtese3Zs4eZM2dywQUX0LNnz1q/V3l5eUyaNIlDDz2U\n9evXc8011zBq1Chee+21ar8PZQNXmzdvTkFBAbfddhuzZs1ixowZdO/enRUrVrB48WIKCgpYs2YN\nF110ETfddBPDhw9n8+bNvPnmm+X1pXLMQJMmTTj66KPrXC5SjQERkfqY+NxCPli1KeHr//npBnbs\n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\nBbBjx45qz9UnrnRo1KgRHTt2xMwyHUq11BgQEUmlV25K260aN25Mx44d6dixI3379uXKK6/kww8/\nZNu2bXWqp7CwsLyeslf8IjkNGzakY8eOdO7cmf79+zNq1CjefPNNNm3aVGV9c+fOpU2bNgCceuqp\nmFn5NrtPPvkkhx9+OO3atWP//ffnxhtvJH79m+7duzNhwgRGjhxJ69atOf/886uNO5G4pk6dynHH\nHUdBQQGFhYWcffbZrFy5Egh28T3kkEP26gJYsmQJZsa7775b5X03btzIJZdcQmFhIQUFBQwYMIC3\n3367Qv6Vuwnuv/9+DjjgAJo1a8awYcP4v//7vyobC9OnT+fggw+moKCA73znO3XqaqiLhimpNUvF\nrUCoPeBDKkq5gvJNhVatWiV1r/mCV2+m5Ngr6lyurnve79y5k127dpWXKSkpYerUqfTu3bvCcYBt\n27ZVWffWrVuB4PvcuHHjKu9TWlrKnj17yst//vnnPP744+Tl5bF169YqP9AOP/xw3nrrLY4//nim\nTp3KCSecQJs2bXjttdc4++yzGT16NA888ADFxcX88pe/pFGjRlx22WVA8AF9++23M3r0aObOnYu7\nVxl7onFt2rSJa665hp49e/LFF18wfvx4zjnnHObMmQPABRdcwH333cell15aXvc999zDEUccQY8e\nPSgpKcHdKS0tLf/6m9/8Ji1btmTGjBm0adOGadOmceqpp/LOO+/QsWPHvb6vf//737n44ouZMGEC\nZ5xxBvPmzeO6664r/3cry2fZsmX86U9/YurUqWzZsoWRI0dy9dVX19jFsn379nr9PxKpxoC7Pwc8\nV1RUNEqrtoVTlHIF5ZsKixYt2nt1uL+MgTUL6l1nwRPn1rnMjv2KaPTt2xO+Pj8/nxdffJFOnToB\nsGXLFvbff39mz569Vz5NmzatcgW8Zs2aAew1cK1169asWLECCJ4+LF68mE6dOrFnz57ypw4///nP\n6dixY7Xxlf2136VLl/Llnu+9914GDBjAzTffTElJCcceeywrVqzgjjvuYPTo0UAwxmHAgAFce+21\nNeafaFw//elPK5Tr1KkTvXr1YuPGjXTt2pXLLruMG2+8kYULF3LiiSeye/dupk+fztixY8u/Z2ZG\n48aNKSgo4OWXX2bBggWsW7eOpk2bAnDUUUfx/PPP89RTT3H11VeXf19btGhBQUEBU6ZM4fTTTy9v\n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\niPt/Cbt0BRZL3mYAAAAASUVORK5CYII=\n",
            "text/plain": [
              "<Figure size 576x360 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    }
  ]
}